summaryrefslogtreecommitdiff
path: root/app/[lng]/evcp/(evcp)/admin/if/items/page.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'app/[lng]/evcp/(evcp)/admin/if/items/page.tsx')
-rw-r--r--app/[lng]/evcp/(evcp)/admin/if/items/page.tsx371
1 files changed, 0 insertions, 371 deletions
diff --git a/app/[lng]/evcp/(evcp)/admin/if/items/page.tsx b/app/[lng]/evcp/(evcp)/admin/if/items/page.tsx
deleted file mode 100644
index 5fa788bd..00000000
--- a/app/[lng]/evcp/(evcp)/admin/if/items/page.tsx
+++ /dev/null
@@ -1,371 +0,0 @@
-import { getOracleConnection } from "@/lib/oracle-db/db";
-import db from "@/db/db";
-import { items } from "@/db/schema/items";
-import { cache } from "react";
-import { Button } from "@/components/ui/button";
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from "@/components/ui/table";
-
-// Oracle 메타데이터와 행 타입 정의
-type OracleColumn = {
- name: string;
-};
-
-type OracleRow = (string | number | null)[];
-
-// PLM(?)의 Oracle DB 에 직접 연결해 데이터를 가져오는 페이지
-// 모든 데이터를 전부 가져오기는 힘들 수 있으므로 샘플 수준으로 가져오는 것을 우선 node로 처리한다.
-
-// Oracle에서 데이터를 가져오는 함수를 캐싱
-const fetchOracleData = cache(async (limit = 100, offset = 0) => {
- const connection = await getOracleConnection();
-
- try {
- // 자재마스터 클래스 정보 테이블 조회 (CMCTB_MAT_CLAS)
- const result = await connection.execute(
- `SELECT
- CLAS_CD,
- CLAS_NM,
- CLAS_DTL,
- PRNT_CLAS_CD,
- CLAS_LVL,
- DEL_ORDR,
- UOM,
- STYPE,
- GRD_MATL,
- CHG_DT,
- BSE_UOM
- FROM SHI1.CMCTB_MAT_CLAS
- WHERE ROWNUM <= :limit + :offset
- OFFSET :offset ROWS`,
- { limit, offset }
- );
-
- // 총 레코드 수 조회
- const countResult = await connection.execute(
- `SELECT COUNT(*) AS TOTAL FROM SHI1.CMCTB_MAT_CLAS`
- );
-
- const totalCount = countResult.rows?.[0]?.[0] || 0;
-
- return {
- rows: result.rows as OracleRow[] || [],
- metadata: result.metaData as OracleColumn[] || [],
- totalCount
- };
- } catch (error) {
- console.error("Oracle 데이터 조회 오류:", error);
- return { rows: [], metadata: [], totalCount: 0 };
- } finally {
- // 연결 종료
- if (connection) {
- try {
- await connection.close();
- } catch (err) {
- console.error("Oracle 연결 종료 오류:", err);
- }
- }
- }
-});
-
-// 전체 데이터를 가져와 Postgres에 삽입하는 함수
-const syncAllDataToPostgres = cache(async () => {
- const BATCH_SIZE = 1000; // 한 번에 처리할 레코드 수
- const MAX_RECORDS = 50000; // 최대 처리할 레코드 수
-
- try {
- // 총 레코드 수 확인
- const { totalCount } = await fetchOracleData(1, 0);
- const recordsToProcess = Math.min(totalCount, MAX_RECORDS);
-
- let processedCount = 0;
- let currentOffset = 0;
-
- // 배치 단위로 처리
- while (processedCount < recordsToProcess) {
- // 오라클에서 데이터 가져오기
- const { rows, metadata } = await fetchOracleData(BATCH_SIZE, currentOffset);
-
- if (!rows.length) break;
-
- // Postgres DB 트랜잭션 시작
- await db.transaction(async (tx) => {
- // Oracle 데이터를 Postgres 스키마에 맞게 변환하여 삽입 (UPSERT)
- for (const row of rows) {
- // 배열 형태의 데이터를 객체로 변환
- const rowObj: Record<string, string | number | null> = {};
- metadata.forEach((col: OracleColumn, index: number) => {
- rowObj[col.name] = row[index];
- });
-
- await tx
- .insert(items)
- .values({
- itemCode: String(rowObj.CLAS_CD || ''),
- itemName: String(rowObj.CLAS_NM || ''),
- description: rowObj.CLAS_DTL ? String(rowObj.CLAS_DTL) : null,
- parentItemCode: rowObj.PRNT_CLAS_CD ? String(rowObj.PRNT_CLAS_CD) : null,
- itemLevel: typeof rowObj.CLAS_LVL === 'number' ? rowObj.CLAS_LVL : null,
- deleteFlag: rowObj.DEL_ORDR ? String(rowObj.DEL_ORDR) : null,
- unitOfMeasure: rowObj.UOM ? String(rowObj.UOM) : null,
- steelType: rowObj.STYPE ? String(rowObj.STYPE) : null,
- gradeMaterial: rowObj.GRD_MATL ? String(rowObj.GRD_MATL) : null,
- changeDate: rowObj.CHG_DT ? String(rowObj.CHG_DT) : null,
- baseUnitOfMeasure: rowObj.BSE_UOM ? String(rowObj.BSE_UOM) : null
- })
- .onConflictDoUpdate({
- target: items.itemCode,
- set: {
- itemName: String(rowObj.CLAS_NM || ''),
- description: rowObj.CLAS_DTL ? String(rowObj.CLAS_DTL) : null,
- parentItemCode: rowObj.PRNT_CLAS_CD ? String(rowObj.PRNT_CLAS_CD) : null,
- itemLevel: typeof rowObj.CLAS_LVL === 'number' ? rowObj.CLAS_LVL : null,
- deleteFlag: rowObj.DEL_ORDR ? String(rowObj.DEL_ORDR) : null,
- unitOfMeasure: rowObj.UOM ? String(rowObj.UOM) : null,
- steelType: rowObj.STYPE ? String(rowObj.STYPE) : null,
- gradeMaterial: rowObj.GRD_MATL ? String(rowObj.GRD_MATL) : null,
- changeDate: rowObj.CHG_DT ? String(rowObj.CHG_DT) : null,
- baseUnitOfMeasure: rowObj.BSE_UOM ? String(rowObj.BSE_UOM) : null,
- updatedAt: new Date()
- }
- });
- }
- });
-
- processedCount += rows.length;
- currentOffset += BATCH_SIZE;
-
- // 진행 상황 업데이트
- const progress = Math.min(100, Math.round((processedCount / recordsToProcess) * 100));
-
- // 임시 상태 저장 (실제 구현에서는 저장소나 상태 관리 도구를 사용)
- console.log(`진행 상황: ${progress}% (${processedCount}/${recordsToProcess})`);
- }
-
- return {
- success: true,
- message: `${processedCount}개의 자재마스터 클래스 정보가 items 테이블로 성공적으로 이관되었습니다.`,
- count: processedCount
- };
- } catch (error) {
- console.error("Postgres 데이터 이관 오류:", error);
- return {
- success: false,
- message: `데이터 이관 중 오류가 발생했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}`,
- count: 0
- };
- }
-});
-
-// 샘플 데이터만 Postgres에 삽입하는 함수
-const syncSampleDataToPostgres = cache(async () => {
- const { rows, metadata } = await fetchOracleData(100, 0);
-
- if (!rows.length) {
- return {
- success: false,
- message: "Oracle에서 가져올 데이터가 없습니다.",
- count: 0
- };
- }
-
- try {
- // Postgres DB 트랜잭션 시작
- await db.transaction(async (tx) => {
- // Oracle 데이터를 Postgres 스키마에 맞게 변환하여 삽입 (UPSERT)
- for (const row of rows) {
- // 배열 형태의 데이터를 객체로 변환
- const rowObj: Record<string, string | number | null> = {};
- metadata.forEach((col: OracleColumn, index: number) => {
- rowObj[col.name] = row[index];
- });
-
- await tx
- .insert(items)
- .values({
- itemCode: String(rowObj.CLAS_CD || ''),
- itemName: String(rowObj.CLAS_NM || ''),
- description: rowObj.CLAS_DTL ? String(rowObj.CLAS_DTL) : null,
- parentItemCode: rowObj.PRNT_CLAS_CD ? String(rowObj.PRNT_CLAS_CD) : null,
- itemLevel: typeof rowObj.CLAS_LVL === 'number' ? rowObj.CLAS_LVL : null,
- deleteFlag: rowObj.DEL_ORDR ? String(rowObj.DEL_ORDR) : null,
- unitOfMeasure: rowObj.UOM ? String(rowObj.UOM) : null,
- steelType: rowObj.STYPE ? String(rowObj.STYPE) : null,
- gradeMaterial: rowObj.GRD_MATL ? String(rowObj.GRD_MATL) : null,
- changeDate: rowObj.CHG_DT ? String(rowObj.CHG_DT) : null,
- baseUnitOfMeasure: rowObj.BSE_UOM ? String(rowObj.BSE_UOM) : null
- })
- .onConflictDoUpdate({
- target: items.itemCode,
- set: {
- itemName: String(rowObj.CLAS_NM || ''),
- description: rowObj.CLAS_DTL ? String(rowObj.CLAS_DTL) : null,
- parentItemCode: rowObj.PRNT_CLAS_CD ? String(rowObj.PRNT_CLAS_CD) : null,
- itemLevel: typeof rowObj.CLAS_LVL === 'number' ? rowObj.CLAS_LVL : null,
- deleteFlag: rowObj.DEL_ORDR ? String(rowObj.DEL_ORDR) : null,
- unitOfMeasure: rowObj.UOM ? String(rowObj.UOM) : null,
- steelType: rowObj.STYPE ? String(rowObj.STYPE) : null,
- gradeMaterial: rowObj.GRD_MATL ? String(rowObj.GRD_MATL) : null,
- changeDate: rowObj.CHG_DT ? String(rowObj.CHG_DT) : null,
- baseUnitOfMeasure: rowObj.BSE_UOM ? String(rowObj.BSE_UOM) : null,
- updatedAt: new Date()
- }
- });
- }
- });
-
- return {
- success: true,
- message: `${rows.length}개의 자재마스터 클래스 정보가 items 테이블로 성공적으로 이관되었습니다.`,
- count: rows.length
- };
- } catch (error) {
- console.error("Postgres 데이터 삽입 오류:", error);
- return {
- success: false,
- message: `데이터 이관 중 오류가 발생했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}`,
- count: 0
- };
- }
-});
-
-// 현재 PostgreSQL DB에 저장된 데이터를 조회하는 함수
-const fetchCurrentPgData = cache(async () => {
- try {
- return await db.select().from(items).limit(100);
- } catch (error) {
- console.error("Postgres 데이터 조회 오류:", error);
- return [];
- }
-});
-
-export default async function ItemsAdminPage() {
- // 데이터 초기 로드
- const { rows: oracleData, metadata, totalCount } = await fetchOracleData(100, 0);
- const pgData = await fetchCurrentPgData();
-
- // 서버 액션으로 샘플 데이터 동기화 수행
- async function handleSyncSample() {
- "use server";
- await syncSampleDataToPostgres();
- // 반환 없이 void로 처리
- }
-
- // 서버 액션으로 전체 데이터 동기화 수행
- async function handleSyncAll() {
- "use server";
- await syncAllDataToPostgres();
- // 반환 없이 void로 처리
- }
-
- return (
- <div className="p-8 space-y-6">
- <h1 className="text-2xl font-bold">Items 테이블 데이터 관리</h1>
- <p className="text-muted-foreground">PLM의 Oracle DB에서 자재마스터 클래스 정보를 Items 테이블로 이관하는 페이지입니다.</p>
-
- <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
- <div className="border rounded-lg">
- <div className="p-4 border-b bg-muted/50">
- <h2 className="text-xl font-semibold">Oracle DB 데이터</h2>
- <p className="text-sm text-muted-foreground">총 {totalCount}개 중 {oracleData.length}개의 레코드</p>
- </div>
-
- <div className="overflow-auto max-h-80 p-4">
- <Table>
- <TableHeader>
- <TableRow>
- <TableHead>클래스코드</TableHead>
- <TableHead>클래스명</TableHead>
- <TableHead>클래스내역</TableHead>
- <TableHead>레벨</TableHead>
- </TableRow>
- </TableHeader>
- <TableBody>
- {oracleData.map((row: OracleRow, idx: number) => {
- // 컬럼 인덱스 찾기
- const getColIndex = (name: string) => metadata.findIndex((col: OracleColumn) => col.name === name);
- const classCdIdx = getColIndex('CLAS_CD');
- const clasNmIdx = getColIndex('CLAS_NM');
- const clasDtlIdx = getColIndex('CLAS_DTL');
- const clasLvlIdx = getColIndex('CLAS_LVL');
-
- return (
- <TableRow key={idx}>
- <TableCell>{row[classCdIdx]}</TableCell>
- <TableCell>{row[clasNmIdx]}</TableCell>
- <TableCell>{row[clasDtlIdx]}</TableCell>
- <TableCell>{row[clasLvlIdx]}</TableCell>
- </TableRow>
- );
- })}
- </TableBody>
- </Table>
- </div>
- </div>
-
- <div className="border rounded-lg">
- <div className="p-4 border-b bg-muted/50">
- <h2 className="text-xl font-semibold">Items 테이블 데이터</h2>
- <p className="text-sm text-muted-foreground">총 {pgData.length}개의 레코드</p>
- </div>
-
- <div className="overflow-auto max-h-80 p-4">
- <Table>
- <TableHeader>
- <TableRow>
- <TableHead>ID</TableHead>
- <TableHead>아이템코드</TableHead>
- <TableHead>아이템명</TableHead>
- <TableHead>레벨</TableHead>
- </TableRow>
- </TableHeader>
- <TableBody>
- {pgData.map((row) => (
- <TableRow key={row.id}>
- <TableCell>{row.id}</TableCell>
- <TableCell>{row.itemCode}</TableCell>
- <TableCell>{row.itemName}</TableCell>
- <TableCell>{row.itemLevel}</TableCell>
- </TableRow>
- ))}
- </TableBody>
- </Table>
- </div>
- </div>
- </div>
-
- <div className="flex flex-col gap-6 mt-8">
- <div className="flex flex-wrap gap-4">
- <form action={handleSyncSample}>
- <Button type="submit" variant="default">
- 샘플 데이터 이관 (100건)
- </Button>
- </form>
-
- <form action={handleSyncAll}>
- <Button type="submit" variant="secondary">
- 전체 데이터 이관 (최대 50,000건)
- </Button>
- </form>
- </div>
-
- <div className="border rounded-lg p-4">
- <h3 className="text-lg font-semibold mb-2">데이터 이관 시 참고사항</h3>
- <ul className="list-disc list-inside space-y-1 text-sm text-muted-foreground">
- <li>샘플 데이터 이관은 100건의 데이터만 Items 테이블에 저장합니다.</li>
- <li>전체 데이터 이관은 1,000건씩 나누어 최대 50,000건까지 처리합니다.</li>
- <li>데이터 양이 많을 경우 이관 작업에 시간이 소요될 수 있습니다.</li>
- <li>기존 데이터는 유지하고 아이템코드가 같은 경우 업데이트합니다 (UPSERT).</li>
- <li>Oracle의 CMCTB_MAT_CLAS 테이블 데이터를 Items 테이블로 매핑합니다.</li>
- </ul>
- </div>
- </div>
- </div>
- );
-} \ No newline at end of file